﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Algorithm;
using YJKGridTopologyCal.Geometries;
using YJKGridTopologyCal.Geometries.Implementation;
using YJKGridTopologyCal.IO;
using YJKGridTopologyCal.Planargraph;
using YJKGridTopologyCal.Utilities;

namespace YJKGridTopologyCal.Operation.Polygonize
{
	public class EdgeRing
	{
		public static EdgeRing FindEdgeRingContaining(EdgeRing testEr, IList<EdgeRing> shellList)
		{
			ILinearRing ring = testEr.Ring;
			Envelope envelopeInternal = ring.EnvelopeInternal;
			EdgeRing edgeRing = null;
			Envelope envelope = null;
			foreach (EdgeRing edgeRing2 in shellList)
			{
				ILinearRing ring2 = edgeRing2.Ring;
				Envelope envelopeInternal2 = ring2.EnvelopeInternal;
				if (edgeRing != null)
				{
					envelope = edgeRing.Ring.EnvelopeInternal;
				}
				if (!envelopeInternal2.Equals(envelopeInternal) && envelopeInternal2.Contains(envelopeInternal) && CGAlgorithms.IsPointInRing(CoordinateArrays.PointNotInList(ring.Coordinates, ring2.Coordinates), ring2.Coordinates) && (edgeRing == null || envelope.Contains(envelopeInternal2)))
				{
					edgeRing = edgeRing2;
					envelope = edgeRing.Ring.EnvelopeInternal;
				}
			}
			return edgeRing;
		}

		[Obsolete("Use CoordinateArrays.PointNotInList instead")]
		public static Coordinate PointNotInList(Coordinate[] testPts, Coordinate[] pts)
		{
			foreach (Coordinate coordinate in testPts)
			{
				if (!EdgeRing.IsInList(coordinate, pts))
				{
					return coordinate;
				}
			}
			return null;
		}

		[Obsolete]
		public static bool IsInList(Coordinate pt, Coordinate[] pts)
		{
			foreach (Coordinate other in pts)
			{
				if (pt.Equals(other))
				{
					return true;
				}
			}
			return true;
		}

		public static List<DirectedEdge> FindDirEdgesInRing(PolygonizeDirectedEdge startDE)
		{
			PolygonizeDirectedEdge polygonizeDirectedEdge = startDE;
			List<DirectedEdge> list = new List<DirectedEdge>();
			do
			{
				list.Add(polygonizeDirectedEdge);
				polygonizeDirectedEdge = polygonizeDirectedEdge.Next;
				Assert.IsTrue(polygonizeDirectedEdge != null, "found null DE in ring");
				Assert.IsTrue(polygonizeDirectedEdge == startDE || !polygonizeDirectedEdge.IsInRing, "found DE already in ring");
			}
			while (polygonizeDirectedEdge != startDE);
			return list;
		}

		public EdgeRing(IGeometryFactory factory)
		{
			this._factory = factory;
		}

		public void Build(PolygonizeDirectedEdge startDE)
		{
			PolygonizeDirectedEdge polygonizeDirectedEdge = startDE;
			do
			{
				this.Add(polygonizeDirectedEdge);
				polygonizeDirectedEdge.Ring = this;
				polygonizeDirectedEdge = polygonizeDirectedEdge.Next;
				Assert.IsTrue(polygonizeDirectedEdge != null, "found null DE in ring");
				Assert.IsTrue(polygonizeDirectedEdge == startDE || !polygonizeDirectedEdge.IsInRing, "found DE already in ring");
			}
			while (polygonizeDirectedEdge != startDE);
		}

		private void Add(DirectedEdge de)
		{
			this._deList.Add(de);
		}

		public bool IsHole
		{
			get
			{
				return this._isHole;
			}
		}

		public void ComputeHole()
		{
			ILinearRing ring = this.Ring;
			this._isHole = CGAlgorithms.IsCCW(ring.Coordinates);
		}

		public void AddHole(ILinearRing hole)
		{
			if (this._holes == null)
			{
				this._holes = new List<ILinearRing>();
			}
			this._holes.Add(hole);
		}

		public void AddHole(EdgeRing holeER)
		{
			holeER.Shell = this;
			ILinearRing ring = holeER.Ring;
			if (this._holes == null)
			{
				this._holes = new List<ILinearRing>();
			}
			this._holes.Add(ring);
		}

		public IPolygon Polygon
		{
			get
			{
				ILinearRing[] array = null;
				if (this._holes != null)
				{
					array = new ILinearRing[this._holes.Count];
					for (int i = 0; i < this._holes.Count; i++)
					{
						array[i] = this._holes[i];
					}
				}
				return this._factory.CreatePolygon(this._ring, array);
			}
		}

		public bool IsValid
		{
			get
			{
				Coordinate[] coordinates = this.Coordinates;
				if (this._ringPts.Length <= 3)
				{
					return false;
				}
				ILinearRing ring = this.Ring;
				return this._ring.IsValid;
			}
		}

		public bool IsIncludedSet
		{
			get
			{
				return this._isIncludedSet;
			}
		}

		public bool IsIncluded
		{
			get
			{
				return this._isIncluded;
			}
			set
			{
				this._isIncluded = value;
				this._isIncludedSet = true;
			}
		}

		private Coordinate[] Coordinates
		{
			get
			{
				if (this._ringPts == null)
				{
					CoordinateList coordinateList = new CoordinateList();
					foreach (DirectedEdge directedEdge in this._deList)
					{
						EdgeRing.AddEdge(((PolygonizeEdge)directedEdge.Edge).Line.Coordinates, directedEdge.EdgeDirection, coordinateList);
					}
					this._ringPts = coordinateList.ToCoordinateArray();
				}
				return this._ringPts;
			}
		}

		public ILineString LineString
		{
			get
			{
				Coordinate[] coordinates = this.Coordinates;
				return this._factory.CreateLineString(this._ringPts);
			}
		}

		public ILinearRing Ring
		{
			get
			{
				if (this._ring != null)
				{
					return this._ring;
				}
				Coordinate[] coordinates = this.Coordinates;
				try
				{
					this._ring = this._factory.CreateLinearRing(this._ringPts);
				}
				catch (Exception)
				{
				}
				return this._ring;
			}
		}

		private static void AddEdge(Coordinate[] coords, bool isForward, CoordinateList coordList)
		{
			if (isForward)
			{
				for (int i = 0; i < coords.Length; i++)
				{
					coordList.Add(coords[i], false);
				}
				return;
			}
			for (int j = coords.Length - 1; j >= 0; j--)
			{
				coordList.Add(coords[j], false);
			}
		}

		public EdgeRing Shell
		{
			get
			{
				if (!this.IsHole)
				{
					return this;
				}
				return this._shell;
			}
			private set
			{
				this._shell = value;
			}
		}

		public bool HasShell
		{
			get
			{
				return this._shell != null;
			}
		}

		public bool IsOuterHole
		{
			get
			{
				return this._isHole && !this.HasShell;
			}
		}

		public bool IsOuterShell
		{
			get
			{
				return this.OuterHole != null;
			}
		}

		public EdgeRing OuterHole
		{
			get
			{
				if (this.IsHole)
				{
					return null;
				}
				for (int i = 0; i < this._deList.Count; i++)
				{
					EdgeRing ring = ((PolygonizeDirectedEdge)((PolygonizeDirectedEdge)this._deList[i]).Sym).Ring;
					if (ring.IsOuterHole)
					{
						return ring;
					}
				}
				return null;
			}
		}

		internal void UpdateIncluded()
		{
			if (this.IsHole)
			{
				return;
			}
			for (int i = 0; i < this._deList.Count; i++)
			{
				EdgeRing shell = ((PolygonizeDirectedEdge)((PolygonizeDirectedEdge)this._deList[i]).Sym).Ring.Shell;
				if (shell != null && shell.IsIncludedSet)
				{
					this.IsIncluded = !shell.IsIncluded;
					return;
				}
			}
		}

		public override string ToString()
		{
			return WKTWriter.ToLineString(new CoordinateArraySequence(this.Coordinates));
		}

		public bool IsProcessed
		{
			get
			{
				return this._isProcessed;
			}
			set
			{
				this._isProcessed = value;
			}
		}

		private readonly IGeometryFactory _factory;

		private readonly List<DirectedEdge> _deList = new List<DirectedEdge>();

		private DirectedEdge lowestEdge;

		private ILinearRing _ring;

		private Coordinate[] _ringPts;

		private List<ILinearRing> _holes;

		private EdgeRing _shell;

		private bool _isHole;

		private bool _isProcessed;

		private bool _isIncludedSet;

		private bool _isIncluded;

		public class EnvelopeComparator : IComparer<EdgeRing>
		{
			public int Compare(EdgeRing r0, EdgeRing r1)
			{
				return r0.Ring.Envelope.CompareTo(r1.Ring.Envelope);
			}
		}
	}
}
